package org.dei.perla.sys.console;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.dei.perla.sys.console.messages.ConsoleMessage;
import org.dei.perla.sys.console.messages.InjectQueryMessage;
import org.dei.perla.sys.console.messages.InspectMessage;
import org.dei.perla.sys.console.messages.TestQueryMessage;
import org.dei.perla.sys.console.messages.VersionMessage;
import org.dei.perla.sys.console.messages.ConsoleMessage.PerLaRequest.CommandType;
import org.dei.perla.sys.console.messages.ConsoleMessage.PerLaResponse.ResultType;
import org.dei.perla.sys.core.AgentRegistry;
import org.dei.perla.sys.core.PerlaRuntime;


/**
 * Classe che contiene metodi statici per inviare e ricevere messaggi
 * e per la conversione dei payload.
 * @author Diego
 *
 */
public class ConsoleUtils {
	
	private static Socket defaultSocket;
	
	public static void setDefaultSocket(Socket defaultSocket) {
		ConsoleUtils.defaultSocket = defaultSocket;
	}

	/**
	 * Converto uno set di byte nel corrispondendente oggetto
	 * @param bytes
	 * @return
	 */
	public static Object toObject(byte[] bytes){
		Object object = null;
		
		try{				
			object = new ObjectInputStream(new ByteArrayInputStream(bytes)).readObject();
		}catch (java.io.IOException ioe) {
				ioe.getMessage();
		}catch (java.lang.ClassNotFoundException cnfe) {
				cnfe.getMessage();
		}
		
		return object;
	}
	
	/**
	 * Metodo interno per la conversione Object -> Byte
	 * @param object
	 * @return
	 */
	public static byte[] toBytes(Object object) {
		
		ByteArrayOutputStream tBaos = new ByteArrayOutputStream();
		
		try {
		 
			ObjectOutputStream oos = new ObjectOutputStream(tBaos);
		    oos.writeObject(object);
		    
		}catch (java.io.IOException ioe) {
			ioe.printStackTrace();
		}
		
		return tBaos.toByteArray();
	}
	
	public static boolean sendRequest(ConsoleMessage.PerLaRequest parRequest, Socket parSocket) {
		
		try { 
			System.out.println("Invio richiesta in corso....\n");
			
			parRequest.writeDelimitedTo(parSocket.getOutputStream());	
			
			
			
			System.out.println("Inviata richiesta!!!\n");
			
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	
	public static boolean sendResponse(ConsoleMessage.PerLaResponse parResponse, Socket parSocket) {
		
		try { 			
			System.out.println("Invio risposta in corso....\n");
			
			parResponse.writeDelimitedTo(parSocket.getOutputStream());			
						
			System.out.println("Inviata richiesta!!!\n");
			return true;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}
	}
	
	public static ConsoleMessage.PerLaRequest receiveRequest(Socket parSocket) {
		try {
			System.out.println("Ricezione richiesta in corso....\n");
			
			return ConsoleMessage.PerLaRequest.parseDelimitedFrom(parSocket.getInputStream());						
			
			
		} catch (Exception e) {
			
			e.printStackTrace();
		}
		return null;
	}
	
	public static ConsoleMessage.PerLaResponse receiveResponse(Socket parSocket) {
		
		try {
			System.out.println("Ricezione risposta in corso....\n");
						
			return ConsoleMessage.PerLaResponse.parseDelimitedFrom(parSocket.getInputStream());
						
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}
	
	public static ConsoleMessage.PerLaResponse receiveResponse() {		
		return receiveResponse(defaultSocket);
	}
	

	private static HashMap<String, String> helpMap = ConsoleUtils.initHelpMap();
	
	public static final String GENERIC_HELP = "syntax: help <command>";

	private static final String INJECT_HELP = "inject";

	private static final String TEST_HELP = "test";

	public static final String INJECT_COMMAND_SUCCESS = "query successful injected";

	public static final String INJECT_COMMAND_FAILURE = "query parsing failed";
	
	

	public static String getHelp(String s){
		if(s == null)
			return GENERIC_HELP;
		if(helpMap.containsKey(s)){
			return helpMap.get(s);
		}
		return GENERIC_HELP;	
	}

	private static HashMap<String, String> initHelpMap() {
		HashMap<String, String> map = new HashMap<String, String>();
		map.put(INJECT_HELP, "syntax: inject \"<A QUERY>\"");
		map.put(TEST_HELP, "syntax: test \"<A QUERY>\"");
		return map;
	}

	public static boolean versionCommandRequest(Socket socket){
		ConsoleMessage.PerLaRequest.Builder builder = ConsoleMessage.PerLaRequest.newBuilder();
		builder.setRequestHeader(CommandType.SHOW_VERSION);
		return ConsoleUtils.sendRequest(builder.build(), socket);
	} 

	public static boolean versionCommandRequest(){
		return versionCommandRequest(defaultSocket);
	} 

	
	public static boolean versionCommandResponse(Socket socket){
		ConsoleMessage.PerLaResponse.Builder builder = ConsoleMessage.PerLaResponse.newBuilder();
		VersionMessage.VersionResponse.Builder queryMessageBuilder = VersionMessage.VersionResponse.newBuilder();
		builder.setResponseHeader(ResultType.VERSION_RESULT);
		queryMessageBuilder.setVersionString(PerlaRuntime.getVersion());
		builder.setVersionResponseMessage(queryMessageBuilder.build());
		
		return ConsoleUtils.sendResponse(builder.build(), socket);
	} 

	public static boolean versionCommandResponse(){
		return versionCommandResponse(defaultSocket);
	}
	
	public static boolean injectCommandRequest(Socket socket, String query_str){
		ConsoleMessage.PerLaRequest.Builder builder = ConsoleMessage.PerLaRequest.newBuilder();
		InjectQueryMessage.InjectQueryRequest.Builder queryMessageBuilder = InjectQueryMessage.InjectQueryRequest.newBuilder();
		builder.setRequestHeader(CommandType.INJECT_QUERY);
		queryMessageBuilder.setQueryToInject(query_str);
		builder.setInjectQueryRequestMessage(queryMessageBuilder.build());
		
		return ConsoleUtils.sendRequest(builder.build(), socket);
	} 

	public static  boolean injectCommandRequest(String query_str){
		return injectCommandRequest(defaultSocket, query_str);
	}
	
	public static boolean injectCommandResponse(Socket socket, String query_str){
		ConsoleMessage.PerLaResponse.Builder builder = ConsoleMessage.PerLaResponse.newBuilder();
		InjectQueryMessage.InjectQueryResponse.Builder queryMessageBuilder = InjectQueryMessage.InjectQueryResponse.newBuilder();
		builder.setResponseHeader(ResultType.INJECT_RESULT);
		queryMessageBuilder.setQueryError(query_str);
		builder.setInjectQueryResponseMessage(queryMessageBuilder.build());
		
		return ConsoleUtils.sendResponse(builder.build(), socket);
	} 

	public static  boolean injectCommandResponse(String query_str){
		return injectCommandResponse(defaultSocket, query_str);
	}

	
	public static boolean testCommandRequest(Socket socket, String query_str){
		ConsoleMessage.PerLaRequest.Builder builder = ConsoleMessage.PerLaRequest.newBuilder();
		TestQueryMessage.TestQueryRequest.Builder queryMessageBuilder = TestQueryMessage.TestQueryRequest.newBuilder();
		builder.setRequestHeader(CommandType.TEST_QUERY);
		queryMessageBuilder.setQueryToTest(query_str);
		builder.setTestQueryRequestMessage(queryMessageBuilder.build());
		
		return ConsoleUtils.sendRequest(builder.build(), socket);
	}

	public static  boolean testCommandRequest(String query_str){
		return testCommandRequest(defaultSocket, query_str);
	}
	
	public static boolean testCommandResponse(Socket socket, String query_str){
		ConsoleMessage.PerLaResponse.Builder builder = ConsoleMessage.PerLaResponse.newBuilder();
		TestQueryMessage.TestQueryResponse.Builder queryMessageBuilder = TestQueryMessage.TestQueryResponse.newBuilder();
		builder.setResponseHeader(ResultType.TEST_RESULT);
		queryMessageBuilder.setQueryError(query_str);
		builder.setTestQueryResponseMessage(queryMessageBuilder.build());

		return ConsoleUtils.sendResponse(builder.build(), socket);
	}
	
	public static  boolean testCommandResponse(String query_str){
		return testCommandResponse(defaultSocket, query_str);
	}
	

	public static boolean inspectRequest(Socket socket, int id) {
		ConsoleMessage.PerLaRequest.Builder builder = ConsoleMessage.PerLaRequest.newBuilder();
		InspectMessage.InspectRequest.Builder requestMessageBuilder = InspectMessage.InspectRequest.newBuilder();
		builder.setRequestHeader(CommandType.INSPECT_COMMAND);
		requestMessageBuilder.setAgentId(id);
		builder.setInspectRequestMessage(requestMessageBuilder.build());
		return ConsoleUtils.sendRequest(builder.build(), socket);
	}
	
	public static boolean inspectRequest(int id){
		return inspectRequest(defaultSocket, id);
	}
	
	@SuppressWarnings("unchecked")
	public static boolean inspectResponse(Socket socket, int id) {
		ConsoleMessage.PerLaResponse.Builder builder = ConsoleMessage.PerLaResponse.newBuilder();
		InspectMessage.InspectResponse.Builder responseMessageBuilder = InspectMessage.InspectResponse.newBuilder();
		builder.setResponseHeader(ResultType.INSPECT_RESULT);
		doInspectUtil(responseMessageBuilder, AgentRegistry.getAgent(id).inspect());
		builder.setInspectResponseMessage(responseMessageBuilder.build());
		return ConsoleUtils.sendResponse(builder.build(), socket);
	}
	
	public static boolean inspectResponse(int id){
		return inspectResponse(defaultSocket, id);
	}
	
	private static void doInspectUtil(InspectMessage.InspectResponse.Builder responseMessageBuilder, ArrayList<String[]> list){
		if(list.size() != 0){
			int colNo = list.get(0).length;
			responseMessageBuilder.setColumnNo(colNo);
			ArrayList<String> support = new ArrayList<String>();
			for (Iterator<String[]> iterator = list.iterator(); iterator.hasNext();) {
				String[] stringA = iterator.next();
				for (int i = 0; i < stringA.length; i++) {
					support.add(stringA[i]);
				}
			}
			responseMessageBuilder.addAllValueStr(support);
		}
		else{
			return;
		}
	}
	
	public static void parseResponse(ConsoleMessage.PerLaResponse response){
		response.getInspectResponseMessage().getValueStrList();
	}
	
}
